package org.qpcr.community.project.web;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.hswebframework.ezorm.core.param.Term;
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
import org.hswebframework.reactor.excel.ReactorExcel;
import org.hswebframework.web.api.crud.entity.PagerResult;
import org.hswebframework.web.api.crud.entity.QueryNoPagingOperation;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.authorization.annotation.Authorize;
import org.hswebframework.web.authorization.annotation.QueryAction;
import org.hswebframework.web.authorization.annotation.Resource;
import org.hswebframework.web.authorization.annotation.SaveAction;
import org.hswebframework.web.bean.FastBeanCopier;
import org.hswebframework.web.crud.query.QueryHelper;
import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController;
import org.qpcr.community.auth.service.OrganizationService;
import org.qpcr.community.auth.service.UserDetailService;
import org.qpcr.community.device.service.LocalDeviceInstanceService;
import org.qpcr.community.io.file.FileManager;
import org.qpcr.community.io.file.FileOption;
import org.qpcr.community.project.enums.ExportType;
import org.qpcr.community.project.enums.FileType;
import org.qpcr.community.project.model.entity.*;
import org.qpcr.community.project.model.entity.excel.*;
import org.qpcr.community.project.service.LocalExperimentalRecordsInstanceService;
import org.qpcr.community.project.service.LocalFileCenterInstanceService;
import org.qpcr.community.elastic.search.service.ElasticSearchService;
import org.qpcr.community.project.web.excel.ExperimentalRecordsExcelFilterColumns;
import org.qpcr.community.project.web.excel.ExperimentalRecordsExcelInfo;
import org.qpcr.community.web.response.ValidationResult;
import org.qpcr.core.device.AuthenticationResponse;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.bind.annotation.*;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuples;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import com.fasterxml.jackson.annotation.JsonProperty;

/**
 * 实验记录接口
 */

@RestController
@RequestMapping({"/project/experimental-records", "/project/experimental/records"})
@Authorize
@Resource(id = "experimental-records-instance", name = "实验记录")
@Slf4j
@Tag(name = "实验记录接口")
@RequiredArgsConstructor
public class ExperimentalRecordsController implements
        ReactiveServiceCrudController<ExperimentalRecordsInstanceEntity, String> {
    @Getter
    private final LocalExperimentalRecordsInstanceService service;
    private final ExperimentalRecordsExcelFilterColumns filterColumns;
    private final FileManager fileManager;
    private final LocalFileCenterInstanceService fileCenterInstanceService;
    private final UserDetailService userDetailService;
    private final LocalDeviceInstanceService deviceInstanceService;
    private final OrganizationService organizationService;
    private final ElasticSearchService elasticSearchService;

    DataBufferFactory bufferFactory = new DefaultDataBufferFactory();


    @PostMapping("/no-auth")
    @SaveAction
    @Operation(
            summary = "新增单个数据,并返回新增后的数据."
    )
    @Authorize(ignore = true)
    public Flux<SaveResult> addOverride(@RequestBody Flux<ExperimentalRecordsInstanceEntity> payload) {
        return payload.flatMap(entity -> userDetailService.getUserDetailById(entity.getCreatorId()).flatMap(userDetail -> {
            entity.setCreatorUserName(userDetail.getUsername());
            entity.setCreatorName(userDetail.getName());
            entity.setModifierId(entity.getCreatorId());
            entity.setModifierName(userDetail.getName());
            entity.setModifyTime(userDetail.getCreateTime());
            if(entity.getType()==null){
                entity.setType(0);
            }
            return Mono.just(entity);
        }).switchIfEmpty(Mono.error(new RuntimeException("用户ID不存在: " + entity.getCreatorId())))
        ).flatMap(entity1 -> deviceInstanceService.getDeviceDetail(entity1.getDeviceId())
                                                  .switchIfEmpty(Mono.error(new RuntimeException("设备ID不存在: " + entity1.getDeviceId())))
                                                  .flatMap(deviceDetail -> {
            entity1.setDeviceName(deviceDetail.getName());
            return this.save(Flux.from(Mono.just(entity1)));
        }));
    }


    /**
     * 导出实验记录数据
     *
     * @param response  response
     * @param parameter parameter
     * @param format    format
     * @return Mono<Void>
     * @throws IOException IOException
     */
    @PostMapping("/export.{format}")
    @QueryAction
    @QueryNoPagingOperation(summary = "导出实验记录数据")
    public Mono<Void> export(ServerHttpResponse response,
                             @RequestBody QueryParamEntity parameter,
                             @PathVariable @Parameter(description = "文件格式,支持csv,xlsx") String format) throws IOException {
        response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=".concat(URLEncoder.encode("实验记录." + format, StandardCharsets.UTF_8
                        .displayName())));
        parameter.setPaging(false);
        return Mono.just(
                        //实验记录
                        service.query(parameter)
                )
                .map(tp1 -> Tuples
                        .of(
                                //表头
                                ExperimentalRecordsExcelInfo.getExportHeaderMapping(filterColumns),
                                Collections.emptyList()
                        ))
                .defaultIfEmpty(Tuples.of(ExperimentalRecordsExcelInfo.getExportHeaderMapping(filterColumns),
                        Collections.emptyList()))
                .flatMapMany(headerAndConfigKey -> ReactorExcel
                        .<ExperimentalRecordsExcelInfo>writer(format)
                        .headers(headerAndConfigKey.getT1())
                        .converter(ExperimentalRecordsExcelInfo::toMap)
                        .writeBuffer(service
                                        .query(parameter)
                                        .flatMap(entity -> Mono.just(FastBeanCopier.copy(entity, new ExperimentalRecordsExcelInfo())))
                                        .buffer(200)
                                        .flatMap(Flux::fromIterable)
                                , 512 * 1024))//缓冲512k
                .doOnError(err -> log.error(err.getMessage(), err))
                .map(bufferFactory::wrap)
                .as(response::writeWith);
    }






    /**
     * 导出物模型实验数据
     *
     * @param recordId  recordId
     * @param experimentalRecordsInstanceEntity experimentalRecordsInstanceEntity
     */
    @PostMapping("/{recordId}/{type}/metadada/export.{format}")
    @QueryAction
    @Authorize(ignore = true)
    @QueryNoPagingOperation(summary = "导出物模型实验数据")
    public Mono<Void> exportModel(ServerHttpResponse response,
        @PathVariable String recordId,@PathVariable String type,
        @RequestBody ExperimentalRecordsInstanceEntity experimentalRecordsInstanceEntity) {
        return ReactorExcel
                .xlsxWriter()
                .multiSheet()
                .sheet(service.getRecordSheet(recordId, experimentalRecordsInstanceEntity,type))
                .sheet(service.getDeviceStateData(experimentalRecordsInstanceEntity,type))
                .sheet(service.getTemperatureReportData(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
                .sheet(service.getVolAndCurrent(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
                .sheet(service.getAmplificationData(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
                .sheet(service.getMeteringDataReport(recordId, experimentalRecordsInstanceEntity.getDeviceId(),false,type))
                .sheet(service.getMeteringSaveDataReport(recordId, experimentalRecordsInstanceEntity.getDeviceId(),false,type))
                .sheet(service.getCT(recordId))
                .sheet(service.getMeltCurveData(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
                .sheet(service.getMeltCurveCuleData(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
                .sheet(service.getTrim(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
                .writeBytes(1024 * 1024)
                .map(bufferFactory::wrap)
                .as(dataBuffer -> {
                    Date receiveDate=new Date(experimentalRecordsInstanceEntity.getReceiveTime());
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
                    String fileName=experimentalRecordsInstanceEntity.getItem()+"_"+experimentalRecordsInstanceEntity.getName()+"_"+experimentalRecordsInstanceEntity.getModuleCode()+"_"+sdf.format(receiveDate)+".xlsx";
                    // 保存附件
                    String finalFileName = fileName;
                    System.out.println("下载文件名称："+fileName);
                    try {
                        response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION,
                                "attachment; filename=".concat(URLEncoder.encode(finalFileName, StandardCharsets.UTF_8
                                        .displayName())));
                    } catch (UnsupportedEncodingException e) {
                        throw new RuntimeException(e);
                    }
                    return fileManager
                            .saveFile(finalFileName, dataBuffer, FileOption.publicAccess)
                            .flatMap(fileInfo -> {
                                FileCenterInstanceEntity fileCenterInstanceEntity = new FileCenterInstanceEntity();
                                fileCenterInstanceEntity.setName(finalFileName);
                                fileCenterInstanceEntity.setType(ExportType.experimentalRecord.getValue());
                                fileCenterInstanceEntity.setFileId(fileInfo.getId());
                                fileCenterInstanceEntity.setConfiguration(fileInfo.getOthers());
                                return fileCenterInstanceService.save(fileCenterInstanceEntity)
                                        .then(response.writeWith(dataBuffer));
                            });
                })
                .doOnError(err -> log.error(err.getMessage(), err));
    }


    /**
     * 导出计算库的熔解扩增表格
     *
     * @param recordId  recordId
     * @param experimentalRecordsInstanceEntity experimentalRecordsInstanceEntity
     */
    @PostMapping("/{recordId}/amplificationMetadada/export.{format}")
    @QueryAction
    @Authorize(ignore = true)
    @QueryNoPagingOperation(summary = "导出计算库的熔解扩增表格")
    public Mono<Void> exportAmplification(ServerHttpResponse response,
                                          @PathVariable String recordId,
                                          @RequestBody ExportAmplificationParam experimentalRecordsInstanceEntity) {
        String type = "zh";

        return ReactorExcel
            .xlsxWriter()
            .multiSheet()
            .sheet(service.getRecordSheet(recordId, experimentalRecordsInstanceEntity, type))
            .sheet(service.getDeviceStateData(experimentalRecordsInstanceEntity, type))
            .sheet(service.getTemperatureReportData(recordId, experimentalRecordsInstanceEntity.getDeviceId(), type))
            .sheet(service.getVolAndCurrent(recordId, experimentalRecordsInstanceEntity.getDeviceId(), type))
            .sheet(service.getAmplificationData(recordId, experimentalRecordsInstanceEntity, type))//扩增替换
            .sheet(service.getMeteringDataReport(recordId, experimentalRecordsInstanceEntity.getDeviceId(), false, type))
            .sheet(service.getMeteringSaveDataReport(recordId, experimentalRecordsInstanceEntity.getDeviceId(), false, type))
            .sheet(service.getCT(recordId, experimentalRecordsInstanceEntity)) //CT替换
            .sheet(service.getMeltCurveData(recordId, experimentalRecordsInstanceEntity.getDeviceId(), type))
            .sheet(service.getMeltCurveCuleData(recordId, experimentalRecordsInstanceEntity, type))//熔解计算后替换
            .sheet(service.getTrim(recordId, experimentalRecordsInstanceEntity, type)) //trim替换
            // 新增sheet
            .sheet(service.getAmplificationAnalysisSheet(experimentalRecordsInstanceEntity))
            .sheet(service.getMeltAnalysisSheet(experimentalRecordsInstanceEntity))
            .writeBytes(1024 * 1024)
            .collectList()
            .flatMap(excelBuffers -> {
                // 处理 Base64 图片
                String base64Image = experimentalRecordsInstanceEntity.getCharts();
                byte[] imageBytes = null;
                if (base64Image != null && !base64Image.isEmpty()) {
                    String base64Data = base64Image.split(",")[1]; // 去除可能的前缀
                    imageBytes = Base64.getDecoder().decode(base64Data);
                }

                // 创建 ZIP 输出流
                ByteArrayOutputStream zipBaos = new ByteArrayOutputStream();
                try (ZipOutputStream zipOut = new ZipOutputStream(zipBaos)) {
                    // 添加 Excel 文件到 ZIP
                    ByteArrayOutputStream excelBaos = new ByteArrayOutputStream();
                    for (byte[] buffer : excelBuffers) {
                        excelBaos.write(buffer);
                    }

                    byte[] excelBytes = excelBaos.toByteArray();
                    zipOut.putNextEntry(new ZipEntry(experimentalRecordsInstanceEntity.generateFileName(FileType.EXCEL_XLSX)));
                    zipOut.write(excelBytes);
                    zipOut.closeEntry();

                    // 添加图片到 ZIP
                    if (imageBytes != null) {
                        zipOut.putNextEntry(new ZipEntry(experimentalRecordsInstanceEntity.generateFileName(FileType.IMAGE_JPG)));
                        zipOut.write(imageBytes);
                        zipOut.closeEntry();
                    }
                } catch (IOException e) {
                    return Mono.error(e);
                }

                byte[] zipBytes = zipBaos.toByteArray();
                String fileName = experimentalRecordsInstanceEntity.generateFileName(FileType.ZIP);

                // 设置响应头
                try {
                    response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION,
                                              "attachment; filename=".concat(URLEncoder.encode(fileName, StandardCharsets.UTF_8.displayName())));
                } catch (UnsupportedEncodingException e) {
                    return Mono.error(e);
                }
                response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);

                return response.writeWith(Mono.just(bufferFactory.wrap(zipBytes)));
            })
            .doOnError(err -> log.error(err.getMessage(), err));
    }

    /**
     * 导出计算库CT值表格
     *
     */
    @PostMapping("/CTMetadada/export.{format}")
    @QueryAction
    @Authorize(ignore = true)
    @QueryNoPagingOperation(summary = "导出计算库的熔解扩增表格")
    public Mono<Void> exportCT(ServerHttpResponse response, @RequestBody CTMetadada ctMetadada ) {

        return ReactorExcel
            .xlsxWriter()
            .multiSheet()
            .sheet(service.getCTMetadada(ctMetadada))
            .sheet(service.getCTMetadada1(ctMetadada))
            .writeBytes(1024 * 1024)
            .map(bufferFactory::wrap)
            .as(dataBuffer -> {

                String fileName="标准分析结果";

                Date receiveDate=new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");

                fileName+="_"+sdf.format(receiveDate)+".xlsx";

                // 保存附件
                String finalFileName = fileName;
                try {
                    response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION,
                                              "attachment; filename=".concat(URLEncoder.encode(fileName, StandardCharsets.UTF_8
                                                  .displayName())));
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);
                }
                return fileManager
                    .saveFile(fileName, dataBuffer, FileOption.publicAccess)
                    .flatMap(fileInfo -> {
                        FileCenterInstanceEntity fileCenterInstanceEntity = new FileCenterInstanceEntity();
                        fileCenterInstanceEntity.setName(finalFileName);
                        fileCenterInstanceEntity.setType(ExportType.experimentalRecord.getValue());
                        fileCenterInstanceEntity.setFileId(fileInfo.getId());
                        fileCenterInstanceEntity.setConfiguration(fileInfo.getOthers());
                        return fileCenterInstanceService.save(fileCenterInstanceEntity)
                                                        .then(response.writeWith(dataBuffer));
                    });
            })
            .doOnError(err -> log.error(err.getMessage(), err));
    }

    /**
     * 导出物模型实验数据
     *
     * @param recordId  recordId
     * @param experimentalRecordsInstanceEntity experimentalRecordsInstanceEntity
     */
    @PostMapping("/{recordId}/metadada/export")
    @QueryAction
    @Authorize(ignore = true)
    @QueryNoPagingOperation(summary = "导出物模型实验数据")
    public Mono<String> exportExcel(ServerHttpResponse response,
                                  @PathVariable String recordId,
                                  @RequestBody ExperimentalRecordsInstanceEntity experimentalRecordsInstanceEntity) {
        String type="zh";
        return  ReactorExcel
            .xlsxWriter()
            .multiSheet()
            .sheet(service.getRecordSheet(recordId, experimentalRecordsInstanceEntity,type))
            .sheet(service.getDeviceStateData(experimentalRecordsInstanceEntity,type))
            .sheet(service.getTemperatureReportData(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
            .sheet(service.getVolAndCurrent(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
            .sheet(service.getAmplificationData(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
            .sheet(service.getMeteringDataReport(recordId, experimentalRecordsInstanceEntity.getDeviceId(),false,type))
            .sheet(service.getMeteringSaveDataReport(recordId, experimentalRecordsInstanceEntity.getDeviceId(),false,type))
            .sheet(service.getCT(recordId))
            .sheet(service.getMeltCurveData(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
            .sheet(service.getMeltCurveCuleData(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
            .sheet(service.getTrim(recordId, experimentalRecordsInstanceEntity.getDeviceId(),type))
            .writeBytes(1024 * 1024)
            .map(bufferFactory::wrap)
            .as(dataBuffer -> {
                LocalDateTime localDateTime = LocalDateTime.now();
                if (experimentalRecordsInstanceEntity.getReceiveTime() != null) {
                    Instant instant = Instant.ofEpochMilli(experimentalRecordsInstanceEntity.getReceiveTime());
                    localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
                }
                String fileName = experimentalRecordsInstanceEntity.getItem();
                if (experimentalRecordsInstanceEntity.getName() != null) {
                    fileName += "_" + experimentalRecordsInstanceEntity.getName();
                }
                Date receiveDate = new Date(experimentalRecordsInstanceEntity.getReceiveTime());
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
                LocalDateTime now = LocalDateTime.now();
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("mmss");

                // 格式化时间
                String formattedDateTime = now.format(formatter);

                fileName += "_" + experimentalRecordsInstanceEntity.getModuleCode() + "_" + sdf.format(receiveDate) +"_"+formattedDateTime+ ".xlsx";

//                    String fileName = FileUtil.getExportFileName(experimentalRecordsInstanceEntity, "xlsx");
                // 保存附件
                String finalFileName = fileName;
                return fileManager
                    .saveFiles(fileName, dataBuffer, FileOption.publicAccess)
                    .flatMap(fileInfo -> {

                        return Mono.just(fileInfo.getStoragePath());
                    });
            }) ;
//         Mono.empty();
    }

    private final QueryHelper queryHelper;


    /**
     * 批量压缩并导出ZIP文件为

     * @return
     */
    @PostMapping("/exportzip")
    @QueryAction
    @Authorize(ignore = true)
    @QueryNoPagingOperation(summary = "导出物模型实验数据")
    public Mono<Void> exportZip(ServerHttpResponse response,@RequestBody String[] filePaths) {
        // 设置响应头
        response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
        response.getHeaders().set("Content-Disposition", "attachment; filename=files.zip");

        // 获取文件路径并压缩成 ZIP
//        Flux<String> filePaths = service.getExcelFilePaths();
//        Flux<String> filePathFlux = Flux.fromArray(filePaths);
//        return service.createZipFromPaths(filePathFlux)
//            .flatMapMany(dataBuffer -> Flux.just(dataBuffer)) // 转换为流式 DataBuffer
//            .flatMap(dataBuffer -> response.writeWith(Mono.just(dataBuffer))); // 写入响应流
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
        Date now=new Date();

        String fileName =sdf.format(now)+".zip";

//                    String fileName = FileUtil.getExportFileName(experimentalRecordsInstanceEntity, "xlsx");
        // 保存附件
        String finalFileName = fileName;
//        try {
//            response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION,
//                                      "attachment; filename=".concat(URLEncoder.encode(fileName, StandardCharsets.UTF_8
//                                          .displayName())));
//        } catch (UnsupportedEncodingException e) {
//            throw new RuntimeException(e);
//        }
                Flux<String> filePathFlux = Flux.fromArray(filePaths);
        return service.createZipFromPaths(filePathFlux)
//                      .flatMapMany(dataBuffer -> Flux.just(dataBuffer)) // 转换为流式 DataBuffer
                      .flatMap(dataBuffer -> {
                          // 保存文件到存储，并返回文件信息
                          return fileManager.saveFiles(fileName, Flux.just(dataBuffer), FileOption.publicAccess)
                                            .flatMap(fileInfo -> {
                                                // 保存文件记录到数据库
                                                FileCenterInstanceEntity fileCenterInstanceEntity = new FileCenterInstanceEntity();
                                                fileCenterInstanceEntity.setName(fileName);
                                                fileCenterInstanceEntity.setType(ExportType.experimentalRecord.getValue());
                                                fileCenterInstanceEntity.setFileId(fileInfo.getId());
                                                fileCenterInstanceEntity.setConfiguration(fileInfo.getOthers());
                                                return fileCenterInstanceService.save(fileCenterInstanceEntity)
                                                                                .then(Mono.just(fileInfo));
                                            })
                                            .flatMap(fileInfo -> {
                                                // 设置 HTTP 响应头并返回文件流
                                                response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
                                                response.getHeaders().setContentDisposition(ContentDisposition
                                                                                                .builder("attachment")
                                                                                                .filename(fileName)
                                                                                                .build());
                                                return response.writeWith(Flux.just(dataBuffer));
                                            });
                      });


    }








    /**
     * 实验记录统计
     *
     * @param startTime 创建时间的开始时间
     * @param endTime   创建时间的结束时间
     * @return Mono<List < ExperimentalRecordsCount>>
     */
    //原生SQL方式
    @GetMapping("/statistics")
    @Authorize(ignore = true)
    @QueryNoPagingOperation(summary = "实验记录统计接口")
    public Mono<List<ExperimentalRecordsCount>> nativeJoinExample(@Parameter Long startTime, @Parameter Long endTime, @Parameter String deviceId) {
        String sql="   select device_id as deviceId , " +
            "CAST(DATE_FORMAT(DATE(FROM_UNIXTIME(receive_time/1000)), '%Y-%m-%d') AS CHAR) as date  ,    " +
            "count(0) as countNum " +
            "from biz_experimental_records   where 1=1 ";
            if (startTime!=null && startTime>0){
                sql+=" and   create_time > "+ startTime ;
            }
            if(endTime!=null && endTime>0){
                sql+=" and  create_time < "+ endTime ;
            }
            if(deviceId!=null ){
                sql+=" and  device_id ="+ deviceId ;
            }
            sql+=" GROUP BY date, deviceId " ;

        return queryHelper
            .select(sql,
                    ExperimentalRecordsCount::new).fetch().collectList();
    }

    @PostMapping("/_query")
    @Authorize(ignore = true)
    @Operation(summary = "使用POST方式查询实验")
    public Mono<PagerResult<ExperimentalRecordsInstanceEntity>> queryPager(@RequestBody Mono<QueryParamEntity> query) {
        return query.flatMap((q) -> {
            return this.queryPager(q);
        });
    }

    @PostMapping("/org/_query")
    @Authorize(ignore = true)
    @Operation(summary = "使用POST方式查询单位的实验记录-api接口")
    public Mono<PagerResult<ExperimentalRecordsInstanceEntity>> queryOrgPager(@RequestBody Mono<QueryParamEntity> query,@RequestParam String key) {
        return organizationService.findByKey(key).flatMap(org -> {
            if (org==null) {
                return Mono.error(new Exception("该key未授权，请确认后再试!"));
            }
            return query.flatMap((q) -> {
                if (q.getTerms() == null) {
                    q.setTerms(new ArrayList<>());
                }
                List<Term> list =new ArrayList<>();
                Term term=new Term();
                term.setColumn("unitId");
                term.setValue(org.getId());
                term.setTermType("eq");
                term.setType(Term.Type.or);
                list.add(term);
                q.getTerms().addAll(list);  // 合并terms
                return this.queryPager(q);
            });
        });
    }

    /**
     * 根据ID修改实名记录名称
     * @param entity
     * @return
     */
    @PostMapping("/updateName")
    @Authorize(ignore = true)
    @Operation(summary = "根据ID修改实名记录名称")
    public Mono<Integer> updateName(@RequestBody(required = true) ExperimentalRecordsInstanceEntity entity) {
        return service.updateObjectName(entity);
    }



    @PostMapping(value = "/upload-excel", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @Authorize(ignore = true)
    @Operation(summary = "上传实验数据Excel文件")
    public Mono<Integer> parsingExcel(@RequestPart("file") FilePart filePart,String deviceId,String moduleId, String item, String projectName, String receiveTime,String id,String productId) {
        return service.parsingExcel(filePart,deviceId,moduleId,item,projectName,receiveTime,id,productId)
                      .doOnSuccess(entity -> log.info("成功解析Excel文件，生成实验记录实体: {}", projectName))
                      .doOnError(error -> log.error("解析Excel文件失败", error));
    }

}
